#include "link/include/client.h" 
//#define _DEBUG
int rando(char* rando)
{
    int i;
    srand(time(0));
    for(i=0;i<SALT_LEN;++i)
    {
        switch(rand()%3){
        case 0:
            rando[i]=rand()%10+'0';
            break;
        case 1:
            rando[i]=rand()%26+'a';
            break;
        case 2:
            rando[i]=rand()%26+'A';
            break;
        }
    }
    return 0;
}
int enroll(int sockfd,puser_t pusr)
{
    char salt[SALT_LEN+1]={0};
    rando(salt);
#ifdef _DEBUG
    printf("salt:%s\n",salt);
#endif
    int len;
    int ret;
    train_t dt; 
    bzero(&dt,sizeof(train_t));
    char* passwd;
    char *encrypted;
    char saltbuf[12]="$1$";//md5编码
    strcat(saltbuf,salt);
    printf("saltbuf:%s=%ld\n",saltbuf,strlen(saltbuf));
    while(1)
    {
        printf("username:");
        fflush(stdout);
        while(1==read(STDIN_FILENO,pusr->username,sizeof(pusr->username)))
        {
            printf("username:");
            fflush(stdout);
        }
        len=strlen(pusr->username);
        pusr->username[len-1]='\0';
        dt.datalen=strlen(pusr->username);
        strcpy(dt.buf,pusr->username);
        send(sockfd,&dt,4+dt.datalen,0);//发送用户名
        recv(sockfd,&ret,4,0);
        printf("ret=%d\n",ret);
        if(1==ret)
        {
            printf("Repeated username!\n");
            bzero(pusr->username,sizeof(pusr->username));
            continue;
        }
        passwd=getpass("passwd");
        encrypted=crypt(passwd,saltbuf);
        bzero(passwd,strlen(passwd));
        printf("encrypted:%s=%ld\n",encrypted,strlen(encrypted));
        dt.datalen=strlen(encrypted);
        strcpy(dt.buf,encrypted);
        send(sockfd,&dt,4+dt.datalen,0);//发送密文
        bzero(&dt,sizeof(train_t));
        printf("Register succeed!\n");//succeed-v; success-n;
        break;
    }
        printf("Login now!\n");
        login(sockfd,pusr);
    return 0;
}
int login(int sockfd,puser_t pusr)
{
    int len;
    int ret;
    train_t dt; 
    char* passwd;
    char salt[SALT_LEN+1]={0};
    char encrypted[64]={0};
    char saltbuf[12]="$1$";
    while(1)
    {
        bzero(&dt,sizeof(train_t));
        printf("username:");
        fflush(stdout);
        while(1==read(STDIN_FILENO,pusr->username,sizeof(pusr->username)))
        {
            printf("username:");
            fflush(stdout);
        }
        len=strlen(pusr->username);
        pusr->username[len-1]='\0';
        dt.datalen=strlen(pusr->username);
        strcpy(dt.buf,pusr->username);
        send(sockfd,&dt,4+dt.datalen,0);//发送用户名
        recv(sockfd,&len,4,0);
        if(0==len){
            printf("No such user\n");
            bzero(pusr->username,sizeof(pusr->username));
            continue;//表示没有该用户
        }else{//接收用户名对应的盐值
            recv(sockfd,salt,len,0);
            strcat(saltbuf,salt);
        }
        //getpass返回的是指向静态区的指针，为不可重入函数
        passwd=getpass("passwd");
        strcpy(encrypted,crypt(passwd,saltbuf));
        //及时清空密码，同时解决getpass指向静态区导致的部分问题
        bzero(passwd,strlen(passwd));
        dt.datalen=strlen(encrypted);
        strcpy(dt.buf,encrypted);
        send(sockfd,&dt,4+dt.datalen,0);//发送密文
        recv(sockfd,&len,4,0);//接收验证结果
        recv(sockfd,&ret,len,0);
        if(0==ret)
        {
            sprintf(pusr->path,"/%s/",pusr->username);
            printf("Login succeed!\n");
            break;
        }else if(1==ret){
            printf("Wrong passwd!\n");
            //解决第一次登录用户密码错误之后，第二次如果换用户名就登不上的问题
            bzero(saltbuf,strlen(saltbuf));
            strcat(saltbuf,"$1$");
        }else{
            printf("ret=%d\n",ret);
            return -1;
        }
    }
    return 0;
}
int trans_file(int sockfd,optdata_t optd)
{
    signal(SIGPIPE,SIG_IGN);
    train_t dt;
    int ret;
    struct stat buf;
    if(-1==stat(optd.arg,&buf))
    {//判断文件是否存在
        perror("stat");
        return 0;
    }
    //先发送命令
    optd.len=strlen(optd.arg);
    strcpy(optd.arg,optd.arg);
    send(sockfd,&optd,8+optd.len,0);
    //发送文件大小
    dt.datalen=sizeof(buf.st_size);
    memcpy(dt.buf,&buf.st_size,dt.datalen);
    send(sockfd,&dt,sizeof(dt.datalen)+dt.datalen,0);
    //获取文件的md5码，并发送给服务器端
    char md5[64]={0};
    md5sum(optd.arg,md5);
    dt.datalen=strlen(md5);
    strcpy(dt.buf,md5);
    send(sockfd,&dt,4+dt.datalen,0);
    //获取服务器端文件是否存在的信号
    recv(sockfd,&ret,4,0);
    if(1==ret)
    {//表示对端文件存在，实现急速秒传
        return 0;
    }
    //发送文件
    int fd=open(optd.arg,O_RDWR);
    char *pstart=(char*)mmap(NULL,buf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if((char*)-1==pstart)
    {
        perror("mmap");
        return 0;
    }
    off_t sended=0;//记录已发送文件大小
    sended+=send(sockfd,pstart,buf.st_size,0);
    munmap(pstart,buf.st_size);
    close(fd);
    if(sended<buf.st_size)
    {
        return -1;
    }
    return 0;
}
int recv_file(int sockfd,char* arg)
{
    int ret;
    int datalen;
    char buf[1024]={0};
    strcpy(buf,arg);
    //接收文件
    int fd;
    off_t asize=0;
    //先创建文件,同时判断本地是否已存在同名文件
    if(-1==(fd=open(buf,O_RDWR|O_CREAT|O_EXCL|O_APPEND,0666)))
    {//发送当前文件大小
        struct stat statbuf;
        fd=open(buf,O_RDWR|O_APPEND);
        stat(buf,&statbuf);
        asize=statbuf.st_size;
        send(sockfd,&asize,sizeof(off_t),0);
    }else{
        send(sockfd,&asize,8,0);
    }
    //接收文件大小,打印进度条
    int i=0;
    char schedule[11]={0};
    off_t fsize,downloadsize=asize,lastsize=asize,fileslice,slice,size=asize;
    recvcycle(sockfd,&datalen,4);
    recvcycle(sockfd,&fsize,datalen);
    printf("Size of upcoming file = %ld Bytes\n",fsize);
    fileslice=fsize/10000;
    slice=fsize/10;
    printf("======|----------|\n");
    for(i=0;i<(double)downloadsize/fsize*10;++i)
    {
        *(schedule+i)='-';
    }
    //打印时间
    struct timeval start,end;
    gettimeofday(&start,NULL);
    while(1)
    {
        //bzero(buf,sizeof(buf));
        ret=recvcycle(sockfd,&datalen,4);
        if(0==ret&&datalen>0)
        {
            ret=recvcycle(sockfd,buf,datalen);
            if(-1==ret)//对方断开recvcycle返回-1
            {
                break;
            }
            pwrite(fd,buf,datalen,downloadsize);
            //打印百分比
            downloadsize+=datalen;
            if((downloadsize-lastsize)>fileslice)
            {
                if((downloadsize-size)>slice)
                {
                    *(schedule+i)='-';
                    size=downloadsize;
                    ++i;
                }
                printf("%6.2f%%%s\r",(double)downloadsize/fsize*100,schedule);
                fflush(stdout);
                lastsize=downloadsize;
            }
        }else if(0==ret&&datalen==0)
        {
            gettimeofday(&end,NULL);
            printf("\r100.00%%----------|\nRecv done! Time used = %ld seconds.\n",end.tv_sec-start.tv_sec);
            break;
        }else{
            break;
        }
    }
    return 0;
}
int recvcycle(int sockfd,void *pstart,int len)
{
    char *p=(char*)pstart;
    int total=0,ret;
    while(total<len)
    {
        ret=recv(sockfd,p+total,len-total,0);
        if(0==ret)//对端断开
        {
            printf("Something wrong happened!\n");
            return -1;
        }
        total+=ret;
    }
    return 0;
}
int md5sum(const char *filename,char *md5)
{
    char cmd[128]={0};
    strcpy(cmd,"md5sum ");
    strcat(cmd,filename);
    FILE *fp = NULL;
    if( (fp = popen(cmd, "r")) == NULL ) {
        perror("popen");
        return -1;
    }
    fgets(md5,33,fp);//fgets读取的字符最多为size-1
    pclose(fp);
    printf("md5: %s\n", md5);
    return 0;
}

